
Type CalAnimation

	Field m_coreAnimation:CalCoreAnimation
	Field m_lastCallbackTimes:Float[]
	Field m_type:Int
	Field m_state:Int
	Field m_time:Float
	Field m_timeFactor:Float
	Field m_weight:Float
	
	
	Method New()
	
		m_coreAnimation = Null
		m_lastCallbackTimes = New Float[0]
		m_type = 0
		m_state = 0
		m_time = 0
		m_timeFactor = 0
		m_weight = 0
		
	
	End Method
	
	
	Function FromCore:CalAnimation(coreAnimation:CalCoreAnimation)
		anim:CalAnimation = New CalAnimation
		anim.m_coreAnimation = coreAnimation
		anim.m_lastCallbackTimes = New Float[0]
		anim.m_type = 0
		anim.m_state = 0
		anim.m_time = 0
		anim.m_timeFactor = 1
		anim.m_weight = 0	


		Local callbackList:CalCallbackRecord[] = coreAnimation.getCallbackList()
		For Local i:Int = 0 To callbackList.length-1
			anim.m_lastCallbackTimes = anim.m_lastCallbackTimes[..anim.m_lastCallbackTimes.length+1]
			anim.m_lastCallbackTimes[anim.m_lastCallbackTimes.length-1] = 0
		Next
		Return anim
	End Function
	
	
	Method getCoreAnimation:CalCoreAnimation()
		Return m_coreAnimation
	End Method
	
	Method getState:Int()
		Return m_state
	End Method
	
	Method getTime:Float()
		Return m_time
	End Method
	
	Method getType:Int()
		Return m_type
	End Method
	
	Method getWeight:Float()
		Return m_weight
	End Method
	
	Method setTime(time:Float)
		m_time = time
	End Method
	
	Method setTimeFactor(timeFactor:Float)
		m_timeFactor = timeFactor
	End Method
	
	Method getTimeFactor:Float()
		Return m_timeFactor
	End Method
	
	Method checkCallbacks(animationTime:Float, model:CalModel)
		Local callbackList:CalCallbackRecord[] = m_coreAnimation.getCallbackList()
		For Local i:Int = 0 To callbackList.length-1
			' support for dynamic adding of callbacks
			If m_lastCallbackTimes.length <= i Then
				m_lastCallbackTimes = m_lastCallbackTimes[..m_lastCallbackTimes.length+1]
				m_lastCallbackTimes[m_lastCallbackTimes.length-1] = animationTime
			EndIf
			callbackList[i].callback.AnimationUpdate(animationTime, model, model.getUserData())
			If animationTime > 0 And animationTime < m_lastCallbackTimes[i] Then		' looped
				m_lastCallbackTimes[i] = m_lastCallbackTimes[i]-m_coreAnimation.getDuration()
			ElseIf animationTime < 0 And animationTime > m_lastCallbackTimes[i] Then	' reverse-looped  
				m_lastCallbackTimes[i] = m_lastCallbackTimes[i]+m_coreAnimation.getDuration()
			EndIf
			If (animationTime >= 0 And animationTime >= m_lastCallbackTimes[i] + callbackList[i].min_interval) Or (animationTime <  0 And animationTime <= m_lastCallbackTimes[i] - callbackList[i].min_interval) Then
				callbackList[i].callback.AnimationUpdate(animationTime, model, model.getUserData())
				m_lastCallbackTimes[i] = animationTime
			EndIf
		Next		
	End Method
	
	Method completeCallbacks(model:CalModel)
		Local callbackList:CalCallbackRecord[] = m_coreAnimation.getCallbackList()
		For Local i:Int = 0 To callbackList.length-1
			callbackList[i].callback.AnimationComplete(model, model.getUserData())
		Next	
	End Method
	
	Method setType(AType:Int)
		m_type = AType
	End Method
	
	Method setState(state:Int)
		m_state = state
	End Method
	
	Method setWeight(weight:Float)
		m_weight = weight
	End Method


End Type


Type CalAnimationCallback Extends CalCallbackRecord

	Method New()
	
	End Method


	Method AnimationUpdate(anim_time:Float, model:CalModel, userData:Object)
		
	End Method
	
	
	Method AnimationComplete(model:CalModel, userData:Object)
		
	End Method
	

End Type

Type CalAnimationAction Extends CalAnimation

	Field m_delayIn:Float
	Field m_delayOut:Float
	Field m_delayTarget:Float
	Field m_weightTarget:Float
	Field m_autoLock:Byte
	
	Method New()
		
	
		m_delayIn = 0
		m_delayOut = 0
		m_delayTarget = 0
		m_weightTarget = 0
		m_autoLock = False		
		
		m_coreAnimation = Null
		m_lastCallbackTimes = New Float[0]
		m_type = 0
		m_state = 0
		m_time = 0
		m_timeFactor = 0
		m_weight = 0		
		
	End Method
	
	Function BuildFromCore:CalAnimationAction(coreAnimation:CalCoreAnimation)
		Local caa:CalAnimationAction = CalAnimationAction(CalAnimationAction.FromCore(coreAnimation))
		
		caa.m_delayIn = 0
		caa.m_delayOut = 0
		caa.m_delayTarget = 0
		caa.m_weightTarget = 0
		caa.m_autoLock = False
		
		caa.setType(CalAnimationType.TYPE_ACTION)	
		Return caa
	End Function

	Method execute:Byte(delayIn:Float, delayOut:Float, weightTarget:Float=1, autoLock:Byte=False)
	

		setState(CalAnimationState.STATE_IN)
		setWeight(0)
		m_delayIn = delayIn
		m_delayOut = delayOut
		setTime(0)
		m_weightTarget = weightTarget
		m_autoLock = autoLock
	
		Return True	
		
	End Method
	
	Method update:Byte(deltaTime:Float)
		' update animation action time
		If getState() <> CalAnimationState.STATE_STOPPED Then
			setTime(getTime() + deltaTime * getTimeFactor())
		EndIf
	
		' handle IN phase
		If getState() = CalAnimationState.STATE_IN Then
			' check if we are still in the IN phase
			If getTime() < m_delayIn Then
				setWeight(getTime() / m_delayIn * m_weightTarget)
			
			Else 
				setState(CalAnimationState.STATE_STEADY)
				setWeight(m_weightTarget)
			EndIf
		EndIf
	
		' handle STEADY
		If getState() = CalAnimationState.STATE_STEADY Then
			' check if we reached OUT phase
			If m_autoLock = False And getTime() >= getCoreAnimation().getDuration() - m_delayOut Then
				setState(CalAnimationState.STATE_OUT)
			' if the anim is supposed to stay locked on last keyframe, reset the time here.
			ElseIf m_autoLock = True And getTime() > getCoreAnimation().getDuration() Then
				setState(CalAnimationState.STATE_STOPPED)
				setTime(getCoreAnimation().getDuration())
			EndIf      
		EndIf
	
		' handle OUT phase
		If getState() = CalAnimationState.STATE_OUT Then
			' check if we are still in the OUT phase
			If getTime() < getCoreAnimation().getDuration() Then
				setWeight((getCoreAnimation().getDuration() - getTime()) / m_delayOut * m_weightTarget)
			Else
				' we reached the end of the action animation
				setWeight(0)
				Return False
			EndIf
		EndIf
	
		Return True		
	End Method
	

End Type

Type CalAnimationCycle Extends CalAnimation

	Field m_targetDelay:Float
	Field m_targetWeight:Float
	

	Method New()

		m_coreAnimation = Null
		m_lastCallbackTimes = New Float[0]
		m_type = 0
		m_state = 0
		m_time = 0
		m_timeFactor = 0
		m_weight = 0
	
	End Method
	
	Function BuildFromCore:CalAnimationCycle(coreAnimation:CalCoreAnimation)
		Local cac:CalAnimationCycle = New CalAnimationCycle
		cac.m_coreAnimation = coreAnimation
		cac.m_lastCallbackTimes = New Float[0]
		cac.m_type = 0
		cac.m_state = 0
		cac.m_time = 0
		cac.m_timeFactor = 1
		cac.m_weight = 0			
		cac.m_targetDelay = 0
		cac.m_targetWeight = 0
		
		cac.setType(CalAnimationType.TYPE_CYCLE)
		cac.setState(CalAnimationState.STATE_SYNC)
	
		cac.setWeight(0)	
		Return cac
	End Function
	
	Method blend:Byte(weight:Float, Delay:Float)
	  m_targetWeight = weight
	  m_targetDelay = Delay		
	End Method
	
	Method setAsync(time:Float, duration:Float)
		' check if thie animation cycle is already async
		If getState() <> CalAnimationState.STATE_ASYNC Then
			If duration = 0 Then
				setTimeFactor(1)
				setTime(0)
			
			Else 
				setTimeFactor(getCoreAnimation().getDuration() / duration)
				setTime(time * getTimeFactor())
			EndIf
	
			setState(CalAnimationState.STATE_ASYNC)
		EndIf	
	
	End Method
	
	Method update:Byte(deltaTime:Float)
		If m_targetDelay <= deltaTime Then 'abs on deltatime?
			' we reached target delay, set to full weight
			setWeight(m_targetWeight)
			m_targetDelay = 0
	
			' check if we reached the cycles end
			If getWeight() = 0 Then
				Return False
			EndIf
		Else
			' not reached target delay yet, interpolate between current and target weight
			Local factor:Float = deltaTime / m_targetDelay
			setWeight((1 - factor) * getWeight() + factor * m_targetWeight)
			m_targetDelay = m_targetDelay-deltaTime
		EndIf
	
		' update animation cycle time if it is in async state
		If getState() = CalAnimationState.STATE_ASYNC Then
			setTime(getTime() + deltaTime * getTimeFactor())
			If getTime() >= getCoreAnimation().getDuration() Then
				setTime((getTime() Mod getCoreAnimation().getDuration())) 'was modf
			EndIf
			If  getTime() < 0 Then
				setTime(getTime() + getCoreAnimation().getDuration())
			EndIf
		EndIf
	
		Return True	
	End Method
	

End Type



Type CalAnimationType
	Const TYPE_NONE:Int = 0 
	Const TYPE_CYCLE:Int = 1 
	Const TYPE_POSE:Int = 2 
	Const TYPE_ACTION:Int = 3	
End Type

Type CalAnimationState
	Const STATE_NONE 	= 0 
	Const STATE_SYNC 	= 1 
	Const STATE_ASYNC 	= 2 
	Const STATE_IN 		= 3 
	Const STATE_STEADY 	= 4 
	Const STATE_OUT 	= 5 
	Const STATE_STOPPED = 6
End Type

Function Modf:Float( a:Float, n:Float )
  Local ret:Float = a - n * Floor( a / n )
  Return ret
End Function
